/**
 *  @file Convert.cpp
 */
//#include "Convert.h"
#include "../text/CFString.h"
#include "../common/CppMemPool.h"
#include "../common/ObjPtrT.h"
#include "../collections/BinBuf.h"

using namespace cppflib::collections;

namespace cppflib
{

namespace convert
{

/**
 *  Convert from a hex-digit character (0 - 10, a - f/A - F) to 
 *  the real numeric value
 */
_DLLAPI int Char2Hex(wc_t c)
{
   int cval = static_cast<int>(c) & 0xffff;

   if (cval >= 0x30 && cval <= 0x39)  // 0 - 9
      return static_cast<int>(cval - 0x30);
   else if (cval >= 0x41 && cval <= 0x46) // A - F
      return static_cast<int>(cval - 0x41 + 10);
   else if (cval >= 0x61 && cval <= 0x66) // a - f
      return static_cast<int>(cval - 0x61 + 10);

   return 0xffff;
}

/**
 *  Convert from a hex-digit character (0 - 10, a - f/A - F) to 
 *  the real numeric value
 */
_DLLAPI int Char2Hex(char c)
{
   int cval = static_cast<int>(c) & 0xff;

   if (cval >= 0x30 && cval <= 0x39)  // 0 - 9
      return static_cast<int>(cval - 0x30);
   else if (cval >= 0x41 && cval <= 0x46) // A - F
      return static_cast<int>(cval - 0x41 + 10);
   else if (cval >= 0x61 && cval <= 0x66) // a - f
      return static_cast<int>(cval - 0x61 + 10);

   return 0xff;
}

/**
 *  Convert from hex number (0 - 15) to hex-digit character
 */
_DLLAPI wc_t Hex2WChar(int a)
{
   if (a >= 10 && a <= 15)
      return static_cast<wc_t>(0x61 + a - 10);

   if (a < 10)
      return static_cast<wc_t>(0x30 + a);

   return 0;
}

/**
 *  Convert from hex number (0 - 15) to hex-digit character
 */
_DLLAPI char Hex2Char(int a)
{
   if (a >= 10 && a <= 15)
      return static_cast<char>(0x61 + a - 10);

   if (a < 10)
      return static_cast<char>(0x30 + a);

   return 0;
}

/********** for 32 bit integer **********/
#define HEX_PREMAXINT32 0xfffffff
#define MAXINT32 2147483647
#define MININT32 (static_cast<int>(~MAXINT32))
// 1 digit less than MAXINT32
#define PREMAXINT32 214748364

/********** for 64 bit integer **********/
#define HEX_PREMAXINT64 I64N(0xfffffffffffffff)
#define MAXINT64 I64N(9223372036854775807)
#define MININT64 (static_cast<i64_t>(~MAXINT64))
// 1 digit less than MAXINT32
#define PREMAXINT64 I64N(922337203685477580)


/**
 *  template function for converting 32/64 bit integer
 *
 *  STYPE -- signed type (int / i64_t)
 *  UTYPE -- unsigned type
 *  MAXVAL -- positive maximum value
 *  MINVAL -- negative minimum value
 *  PREMAXVAL -- the maximum value in decimal with the least sig digit missing
 *  HEX_PREMAXVAL -- the maximum value in hex with the least sig digit missing  
 */
template <class STYPE, class UTYPE, 
          STYPE MAXVAL, STYPE MINVAL, STYPE PREMAXVAL, UTYPE HEX_PREMAXVAL>
STYPE ToInteger(pcwstr_t str, bool &isOverflow)
{
   STYPE num = 0;
   isOverflow = false;

   if (CFString::WStrncmp(str, _S("0x"), 2) == 0) {
      // hex number assumes no negative
      str += 2;

      UTYPE tempNum = 0;
      wc_t c;
      STYPE v;
      do {
         c = *str++;
         v = Char2Hex(c);
         if (v > 15)
            break;

         if (tempNum > HEX_PREMAXVAL) {
            isOverflow = true;
            break;
         }

         tempNum = (tempNum << 4) | v;

      } while(true);

      num = (isOverflow) ? ~(static_cast<int>(0)) : tempNum;
      
   }
   else {
      // normal decimal number
      wc_t c;
      bool isNeg = (*str == _S('-'));

      if (isNeg)
         str++;

      do {
         c = *str++;
         if (c < _S('0') || c > _S('9'))
            break;

         if (num > PREMAXVAL) {
            isOverflow = true;
            break;
         }
         else if (num == PREMAXVAL) {
            if ((!isNeg && (c - _S('0') > 7)) || 
                 (isNeg && (c - _S('0') > 8))    ) {  // if negative, it ends with '8'
              isOverflow = true;
              break;
            }
         }

         num = num * 10 + (c - _S('0'));
      } while (true);

      if (isOverflow) {
         num = isNeg ? MINVAL : MAXVAL;
      }
      else {
         if (isNeg)
            num = -num;
      }

   }

   return num;	
}



/**
 *  Convert string to integer (+/-)
 * 
 *  @param [in] str -- can be decimal or hex (starts with '0x')
 *  @param [out] isOverflow -- return whether overflow occur
 *
 *  @note If overflow occurs, the max/min value is returned.
 *        Hex number has no negative
 */
_DLLAPI int ToInt32(pcwstr_t str, bool &isOverflow)
{
   isOverflow = false;

   if (!str)
      return 0;

   return ToInteger<int, u32_t, MAXINT32, MININT32, PREMAXINT32, HEX_PREMAXINT32>(str, isOverflow);
}

_DLLAPI int ToInt32(pcwstr_t str)
{
   bool isOverflow = false;
   return ToInt32(str, isOverflow);
}

/**
 *  Convert string to 64 bit integer (+/-)
 *  If overflow occurs, the max/min value is returned
 * 
 *  @param [in] str -- can be decimal or hex (starts with '0x')
 *  @param [out] isOverflow -- return whether overflow occur
 */
_DLLAPI i64_t ToInt64(pcwstr_t str, bool &isOverflow)
{
   isOverflow = false;

   if (!str)
      return 0;

   return ToInteger<i64_t, u64_t, MAXINT64, MININT64, PREMAXINT64, HEX_PREMAXINT64>(str, isOverflow);
}

_DLLAPI i64_t ToInt64(pcwstr_t str)
{
   bool isOverflow = false;
   return ToInt64(str, isOverflow);
}

/**
 *  Extract components of double precision
 */
PRIVATE void __ExtractFloat(double v, u32_t *sign, u32_t *exponent, i64_t *fraction)
{
   u64_t t = *reinterpret_cast<u64_t*>(&v);
   *sign = static_cast<u32_t>((t >> 63) & I64N(0x01));
   *exponent = static_cast<u32_t>((t >> 52) & I64N(0x7ff));
   *fraction = t & I64N(0xfffffffffffff);
}

/**
 *  Extract components of single precision
 */
PRIVATE void __ExtractFloat(float v, u32_t *sign, u32_t *exponent, u32_t *fraction)
{
   u32_t t = *reinterpret_cast<u32_t*>(&v);
   *sign = static_cast<u32_t>((t >> 31) & 0x01);
   *exponent = static_cast<u32_t>((t >> 23) & 0xff);
   *fraction = t & 0x7fffff;
}

_DLLAPI bool IsNaN(double v)
{
   u32_t sign, exponent;
   i64_t fraction;
   __ExtractFloat(v, &sign, &exponent, &fraction);
   return (exponent == 0x7ff && fraction != I64N(0));
}

_DLLAPI bool IsNaN(float v)
{
   u32_t sign, exponent, fraction;
   __ExtractFloat(v, &sign, &exponent, &fraction);
   return (exponent == 0xff && fraction != 0);
}

_DLLAPI bool IsInfinity(double v)
{
   u32_t sign, exponent;
   i64_t fraction;
   __ExtractFloat(v, &sign, &exponent, &fraction);
   return (exponent == 0x7ff && fraction == I64N(0));
}

_DLLAPI bool IsInfinity(float v)
{
   u32_t sign, exponent, fraction;
   __ExtractFloat(v, &sign, &exponent, &fraction);
   return (exponent == 0xff && fraction == 0);
}

_DLLAPI bool IsZero(double v)
{
   u32_t sign, exponent;
   i64_t fraction;
   __ExtractFloat(v, &sign, &exponent, &fraction);
   return (exponent == 0 && fraction == I64N(0));
}

_DLLAPI bool IsZero(float v)
{
   u32_t sign, exponent, fraction;
   __ExtractFloat(v, &sign, &exponent, &fraction);
   return (exponent == 0 && fraction == 0);
}

/*
//// single ////
#define FLOAT_SIGDIGITS 7
#define FLOAT_MAX (3.402823e+38)
#define FLOAT_MIN (-3.402823e+38)
#define FLOAT_MAXPOWER 38
#define FLOAT_MINPOWER (-45)
#define FLOAT_MAXMANTISSA 3.4
#define FLOAT_MINMANTISSA 1.5


//// double ////
#define DOUBLE_SIGDIGITS 15
#define DOUBLE_MAX (1.79769313486232e+308)
#define DOUBLE_MIN (-1.79769313486232e+308)
#define DOUBLE_MAXPOWER 308
#define DOUBLE_MINPOWER (-324)
#define DOUBLE_MAXMANTISSA 1.7
#define DOUBLE_MINMANTISSA 5.0
*/

//! float parsing state
typedef enum _floatparse
{
   BEFOREPOINT,
   AFTERPOINT,
   AT_E,
   AFTER_E
} floatparse_t;

/**
 *  template for converting string to floating point number
 *
 *  FLOATTYPE -- float or double
 */
template <class FLOATTYPE>
FLOATTYPE ToFloatNum(pcwstr_t str)
{
   if (!str)
      return 0;

   wc_t c;
   
   FLOATTYPE fNum = 0.0, fAfterPoint = 0.0;
   int nrAfterPoint = 1;
   int exponent = 0;
   bool isNeg = (*str == _S('-'));
   bool isExpNeg = false;

   floatparse_t fParseState = BEFOREPOINT;
   bool isCont = true;
   

   if (isNeg)
      str++;

   while (isCont) {
      c = *str++;
      
      switch(fParseState) {
         case BEFOREPOINT:
            if (IsInfinity(fNum) || IsNaN(fNum)) {
               isCont = false;
               break;
            }

            if (c >= _S('0') && c <= _S('9')) {
               fNum *= 10;
               fNum += static_cast<int>(c - _S('0'));
            }
            else if (c == _S('.')) {
               fParseState = AFTERPOINT;
            }
            else if (c == _S('e') || c == _S('E')) {
               fParseState = AT_E;
            }
            else {
               isCont = false;
            }

            break;
         case AFTERPOINT:
            if (c >= _S('0') && c <= _S('9')) {
               FLOATTYPE tNum = static_cast<FLOATTYPE>(static_cast<int>(c - _S('0')));
               for (int i = 0; i < nrAfterPoint; ++i)
                  tNum *= static_cast<FLOATTYPE>(0.1);

               fAfterPoint += tNum;
               ++nrAfterPoint;
            }
            else if (c == _S('e') || c == _S('E')) {
               fParseState = AT_E;
            }
            else {
               isCont = false;
            }
            break;
         case AT_E:
            if (c >= _S('0') && c <= _S('9')) {
               fParseState = AFTER_E;
               str--;
            }
            else if (c == _S('+')) {
               fParseState = AFTER_E;
            }
            else if (c == _S('-')) {
               fParseState = AFTER_E;
               isExpNeg = true;
            }
            else {
               isCont = false;
            }
            break;
         case AFTER_E:
            if (static_cast<u32_t>(exponent) >= MAXINT32) {
               isCont = false;
               break;
            }

            if (c >= _S('0') && c <= _S('9')) {
               exponent *= 10;
               exponent += static_cast<int>(c - _S('0'));
            }
            else {
               isCont = false;
            }
            break;
      } // end of switch

   } // end of while


   fNum += fAfterPoint;
   if (isNeg)
      fNum = -fNum;

   if (!IsInfinity(fNum) && !IsNaN(fNum)) {
      if (exponent != 0) {  
         // doesn't check range of exponent and just let it go to infinity
         if (isExpNeg) {
            for (int i = 0; i < exponent; ++i)
               fNum *= static_cast<FLOATTYPE>(0.1);
         }
         else {
            for (int i = 0; i < exponent; ++i)
               fNum *= 10;
         }
      }
   }

   return fNum;
}

/**
 *  Convert string to float (+/-)
 * 
 *  @param [in] str -- can be normal decimal format (e.g. 34.002) or scientific notation (e.g. 3.4002e+001)
 */
_DLLAPI float ToFloat(pcwstr_t str)
{
   if (!str)
      return 0;

   return ToFloatNum<float>(str);
}

/**
 *  Convert string to double (+/-)
 * 
 *  @param [in] str -- can be normal decimal format (e.g. 34.002) or scientific notation (e.g. 3.4002e+001)
 */
_DLLAPI double ToDouble(pcwstr_t str)
{
   if (!str)
      return 0;

   return ToFloatNum<double>(str);
}

_DLLAPI wc_t ToWUpper(wc_t c)
{
   return (c >= _S('a') && c <= _S('z')) ? (c + static_cast<int>(_S('A') - _S('a'))): c;
}

_DLLAPI wc_t ToWLower(wc_t c)
{
   return (c >= _S('A') && c <= _S('Z')) ? (c + static_cast<int>(_S('a') - _S('A'))): c;
}

_DLLAPI char ToUpper(char c)
{
   return (c >= 'a' && c <= 'z') ? (c + static_cast<int>('A' - 'a')): c;
}

_DLLAPI char ToLower(char c)
{
   return (c >= 'A' && c <= 'Z') ? (c + static_cast<int>('a' - 'A')): c;
}


_DLLAPI CFString ToString(int a)
{
   return CFString::SFormat(_S("%d"), a);
}

_DLLAPI CFString ToString(u32_t a)
{
   return CFString::SFormat(_S("%u"), a);
}

_DLLAPI CFString ToString(i64_t a)
{
   return CFString::SFormat(_S("%lld"), a);
}

_DLLAPI CFString ToString(u64_t a)
{
   return CFString::SFormat(_S("%llu"), a);
}

/**
 *  @param [in] szFormat -- user provided format, e.g. "%2.6f"
 */
_DLLAPI CFString ToString(float a, pcwstr_t szFormat)
{
   return CFString::SFormat(szFormat, a);
}

/**
 *  @param [in] szFormat -- user provided format, e.g. "%2.6f"
 */
_DLLAPI CFString ToString(double a, pcwstr_t szFormat)
{
   return CFString::SFormat(szFormat, a);
}

_DLLAPI CFString ToString(float a)
{
   return ToString(a, _S("%f"));
}

_DLLAPI CFString ToString(double a)
{
   return ToString(a, _S("%f"));
}


PRIVATE bool __IsLegalChar(u8_t b)
{
   if ((b >= '0' && b <= '9') || (b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z') || 
        b == '.' || b == '-' || b == '*' || b == '_') {
       return true;
   }
   return false;	
}

/**
 *  Convert orgStr to URL encoded string
 * 
 *  @param [in] orgStr -- original string
 *  @param [in, out] retStr -- to receive converted string
 */
_DLLAPI void ToUrlEncodedString(const CFString & orgStr, CFString & retStr)
{
   retStr = _S("");
   if (orgStr.IsEmpty())
      return;

   char buf[4] = {0};
   ObjPtrT<char> pOrgStr(orgStr.ToUtf8String());
   for (int i = 0; pOrgStr.Ptr()[i]; ++i) {
      if (__IsLegalChar(pOrgStr.Ptr()[i])) {
         buf[0] = pOrgStr.Ptr()[i];
         buf[1] = '\0';
      }
      else {
         buf[0] = '%';
         buf[1] = Hex2Char((pOrgStr.Ptr()[i] >> 4) & 0x0f);
         buf[2] = Hex2Char(pOrgStr.Ptr()[i] & 0x0f);
         buf[3] = '\0';
      }  

      retStr += static_cast<const char *>(buf);
   }

}

/**
 *  Decode URL encoded orgStr to normal string
 * 
 *  @param [in] orgStr -- original string
 *  @param [in] offset -- starting offset of original string
 *  @param [in] length -- length of original string to be read starting at offset
 *  @param [in, out] retStr -- to receive converted string
 */
_DLLAPI void DecodeUrlEncodedString(const CFString & orgStr, int offset, int length, CFString & retStr)
{
   retStr = _S("");
   if (orgStr.IsEmpty())
      return;

   u8_t data;
   BinBuf binBuf(64);
   pcwstr_t pOrgStr = static_cast<pcwstr_t>(orgStr) + offset;
   pcwstr_t pOrgStrEnd = pOrgStr + length;
   while (pOrgStr < pOrgStrEnd) {
      wc_t c = *pOrgStr++;
      if (c == _S('+')) {
         data = ' ';
         binBuf.AppendData(&data, 1);
      }
      else if (c == _S('%')) {
         if (pOrgStr + 2 > pOrgStrEnd)
            break;
         
         int intData = -1;
         int v = Char2Hex(*pOrgStr++);
         if (v <= 0x0f) intData = (v << 4);

         v = Char2Hex(*pOrgStr++);
         if (v <= 0x0f) intData |= v;

         if (intData > 0) {
            data = static_cast<u8_t>(intData);
            binBuf.AppendData(&data, 1);
         }
      }
      else {
         data = static_cast<u8_t>(c);
         binBuf.AppendData(&data, 1);
      }
   }

   data = 0;
   binBuf.AppendData(&data, 1); // make it zero-terminated
   retStr += reinterpret_cast<const char*>(binBuf.GetRawBuf());
}


}  // end of namespace convert


}  // end of namespace cppflib
